package com.moyu.activity;

import com.moyu.control.PlayerControl;
import com.moyu.frame.FrameSavePoint;
import com.moyu.music.MusicPlayer;
import com.moyu.ui.*;

import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Random;

public class PanelGame extends JPanel {
    private Boolean pointFrame;        //计分框是否显示
    private Point[] actPoint;         //操作的当前小方块
    private final Boolean[][] mMap;  //表示该点是否被方块占据
    private final int[][] colorMap; //记录该点颜色
    private Boolean alreadyloose;  //记录游戏是否结束
    private Boolean StartGame;    //记录是否开始游戏
    private int origincode;      //当前方块
    private int actcode;        //下一个即将到来的方块
    private int mapcode;       //颜色摆放中记录颜色
    private final int fontx;  //寻找方块坐标x
    private final int fonty; //寻找方块坐标y
    private final int size; //方块大小
    private int totalpoint;//记录总分
    private int sleeptime;//记录小方块下落速度
    Thread gamethread;   //游戏线程




    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        //返回键盘焦点,游戏失败则不返回焦点
        if (!alreadyloose) this.requestFocus();
        //game over
        Image gameover = new ImageIcon("resources/images/gameover.png").getImage();
        //背景
        Image e = new ImageIcon("resources/images/bg2.jpg").getImage();
        g.drawImage(e, 0, 0, null);

        //框
        //中间主游戏模块宽为32x10=320，高为32x20=640,坐标参数为左上角坐标
        new LeftLayTop(50, 16, 200, 208, this).create(g);//左上
        new LeftLayMid(50, 254, 200, 288).create(g);//左中
        new LeftLayBottom(50, 566, 200, 96, this).create(g);//左下
        new CenterLay(292, 16, 328, 648).create(g);//中间
        new RightLayTop(670, 16, 200, 176, this).create(g);//右上
        new RightLayMid(670, 216, 200, 300).create(g);//右中
        new RightLayBottom(670, 540, 200, 124, this).create(g);//右下

        if (!alreadyloose) {
            //判断失败
            LooseGame();
            //绘制方块
            DrawPoint(g);
            //绘制提示
            DrawHint(g);
            //绘制下一方块
            RightLayTop.DrawNextPoint(g);
        }
        //绘制已落下方块
        DrawMapPoint(g);
        if (alreadyloose) {
            //游戏结束显示计分框
            if (!pointFrame) {
                new FrameSavePoint(this);
                pointFrame = true;
            }
            //游戏结束方块变白
            g.drawImage(gameover, 305, 120, 310, 120, null);
        }
    }

    public PanelGame(Boolean musicon) {
        fontx = 300;
        fonty = 24;
        size = 32;
        //游戏尚未开始
        StartGame = false;
        //游戏尚未失败
        alreadyloose = false;
        //初始下落速度
        sleeptime = 500;
        //初始分数清零
        totalpoint = 0;
        //计分框是否已显示过
        pointFrame = false;
        //刷新初始方块
        init(new Random().nextInt(7));
        //创建按键监听
        this.addKeyListener(new PlayerControl(this));
        //创建判断方块mMap和判断颜色colorMap，初始化
        this.mMap = new Boolean[10][22];
        this.colorMap = new int[10][22];
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 20; j++) {
                mMap[i][j] = false;
                colorMap[i][j] = -1;
            }
        }
        //播放音乐，判断以保证下次开始时音乐不会断
        if (!musicon) {
            musicPlay("Background");
        }

    }

    //绘制方块形状及颜色
    public void DrawPoint(Graphics g) {
        Image img = new ImageIcon("resources/images/point.png").getImage();
        Point[] mypoint = actPoint;
        for (int i = 0; i < mypoint.length; i++) {
            g.drawImage(img, fontx + mypoint[i].x * size, fonty + mypoint[i].y * size, fontx + mypoint[i].x * size + size, fonty + mypoint[i].y * size + size, (origincode + 1) * size, 0, (origincode + 2) * size, size, null);
        }
    }

    //左右判断边缘事件
    private Boolean boolevent(int eventnum, int movex) {
        for (int i = 0; i < actPoint.length; i++)
            if (actPoint[i].x == eventnum || mMap[actPoint[i].x + movex][actPoint[i].y])
                return true;
        return false;
    }

    //按键左←←←←
    public void toright() {
        if (boolevent(9, 1))
            return;
        for (int i = 0; i < actPoint.length; i++)
            actPoint[i].x += 1;
    }

    //按键右→→→→
    public void toleft() {
        if (boolevent(0, -1))
            return;
        for (int i = 0; i < actPoint.length; i++)
            actPoint[i].x -= 1;
    }

    //按键下↓↓↓↓
    public void todown() {
        int maxY = 19;
        boolean flag = false;       //判断当前落点是否为面板底部
        int flagtype = 0;           //判断方块样式，获取高度

        //得到当前方块和下方堆积方块的间距maxY
        for (int i = 0; i < actPoint.length; i++) {
            int NowX = actPoint[i].x;
            for (int y = 19; y > actPoint[i].y; y--) {
                if (mMap[NowX][y]) {
                    flag = true;
                    if (maxY > y - actPoint[i].y)
                        maxY = y - actPoint[i].y;
                }
            }
            //使用flagtype分别处理高度为1,2,3的方块落到底部的情况
            if (actPoint[i].y > flagtype) flagtype = actPoint[i].y;
        }


        //处理初始落到底部的特殊情况
        if (!flag)
            maxY = maxY - flagtype + 1;
        //判断一边为落到实体另一边为落到底部的特殊情况
        for (int i = 0; i < actPoint.length; i++) {
            if (actPoint[i].y + maxY - 1 > 19)
                maxY -= 1;
        }

        //方块落下
        for (int i = 0; i < actPoint.length; i++) {
            //方块占有一格，因而减一
            actPoint[i].y += maxY - 1;
            mMap[actPoint[i].x][actPoint[i].y] = true;
        }
        //获取当前方块形状
        mapcode = origincode;
        //消行机制
        for (int k = 0; k < 19; k++) {
            for (int i = 19; i >= 0; i--) {
                int linesum = 0;
                for (int j = 0; j < 10; j++) {
                    if (mMap[j][i])
                        linesum += 1;
                    else
                        break;
                }
                if (linesum == 10) {
                    musicPlay("Bingo");
                    totalpoint += 10;
                    for (int x = i - 1; x >= 0; x--) {
                        for (int y = 0; y < 10; y++) {
                            mMap[y][x + 1] = mMap[y][x];
                            colorMap[y][x + 1] = colorMap[y][x];
                        }
                    }
                }
            }
        }
        //刷新方块
        init(actcode);
    }

    /**
     * 顺时针旋转算法：笛卡尔坐标公式中逆时针算法（数学坐标系和屏幕坐标系相反）
     * A.x=o.y+o.x-B.y;
     * A.y=o.y-o.x+B.y;
     * o为中心点;
     */
    //按键上↑↑↑↑
    public void toup() {
        //边缘旋转方块偏移量，默认为0，即为常规状态下的旋转
        int offset = 0;
        //正方形方块无法旋转
        if (origincode == 6) return;
        //获取偏移量
        for (int i = 1; i < actPoint.length; i++) {
            int newX = actPoint[0].y + actPoint[0].x - actPoint[i].y;
            if (newX < 0) {
                offset = -Math.min(-offset, newX);
            }
            if (newX > 9) {
                offset = Math.min(offset, 9 - newX);
            }
        }
        //判断限定旋转条件,必须保证所有判断完后再进行旋转变换
        for (int i = 0; i < actPoint.length; i++) {
            int newX = actPoint[0].y + actPoint[0].x - actPoint[i].y;
            int newY = actPoint[0].y - actPoint[0].x + actPoint[i].x;
            if (newX >= 0 && newX <= 9) {
                if (newY < 0 || newY > 19 || mMap[newX + offset][newY]) {
//                    System.out.println("不能旋转,x:" + newX + " y:" + newY);
                    return;
                }
            }
        }
        //在确定了能够旋转之后对方块坐标进行操作
        if (offset != 0) {
            for (int i = 0; i < actPoint.length; i++) {
                actPoint[i].x += offset;
//                System.out.println("移动一格！");
            }
        }
        //旋转
        for (int i = 1; i < actPoint.length; i++) {
            int newX = actPoint[0].y + actPoint[0].x - actPoint[i].y;
            int newY = actPoint[0].y - actPoint[0].x + actPoint[i].x;
            actPoint[i].x = newX;
            actPoint[i].y = newY;
        }
    }

    //刷新方块
    public void init(int actCode) {
        switch (actCode) {
            //长条
            case 0:
                actPoint = new Point[]{new Point(4, 0), new Point(3, 0), new Point(5, 0), new Point(6, 0),};
                break;
            //凸字
            case 1:
                actPoint = new Point[]{new Point(4, 0), new Point(3, 0), new Point(5, 0), new Point(4, 1),};
                break;
            //左L
            case 2:
                actPoint = new Point[]{new Point(4, 0), new Point(3, 0), new Point(5, 0), new Point(3, 1),};
                break;
            //右L
            case 3:
                actPoint = new Point[]{new Point(4, 0), new Point(3, 0), new Point(5, 0), new Point(5, 1),};
                break;
            //左折
            case 4:
                actPoint = new Point[]{new Point(4, 0), new Point(3, 0), new Point(4, 1), new Point(5, 1),};
                break;
            //右折
            case 5:
                actPoint = new Point[]{new Point(4, 0), new Point(3, 1), new Point(4, 1), new Point(5, 0),};
                break;
            //正方
            case 6:
                actPoint = new Point[]{new Point(4, 0), new Point(3, 0), new Point(3, 1), new Point(4, 1),};
                break;
        }
        //保存方块形状以打印
        origincode = actCode;
        //获取下一个方块
        Random r = new Random();
        actcode = r.nextInt(7);
    }

    //绘制已落下方块颜色
    public void DrawMapPoint(Graphics g) {
        Image img = new ImageIcon("resources/images/point.png").getImage();
        for (int y = 19; y >= 0; y--) {
            for (int x = 0; x < 10; x++) {
                if (mMap[x][y]) {
                    if (alreadyloose) {
                        g.drawImage(img, fontx + x * size, fonty + y * size, fontx + x * size + size, fonty + y * size + size, 0, 0, size, size, null);
                    } else {
                        if (colorMap[x][y] == -1) {
                            g.drawImage(img, fontx + x * size, fonty + y * size, fontx + x * size + size, fonty + y * size + size, (mapcode + 1) * size, 0, (mapcode + 2) * size, size, null);
                            colorMap[x][y] = mapcode;
                        } else {
                            g.drawImage(img, fontx + x * size, fonty + y * size, fontx + x * size + size, fonty + y * size + size, (colorMap[x][y] + 1) * size, 0, (colorMap[x][y] + 2) * size, size, null);
                        }
                    }
                }
            }
        }
    }


    //游戏结束事件
    public void LooseGame() {
        for (int i = 0; i < 10; i++) {
            if (mMap[i][0]) {
//                System.out.println("Game Over!");
                alreadyloose = true;
                StartGame = false;
                break;
            }
        }
    }

    //方块下落线程
    public void GameThread(PanelGame panelGame) {
        gamethread = new Thread(() -> {
            while (!alreadyloose && StartGame) {
                boolean flag = false;
                try {
                    Thread.sleep(sleeptime);
                    //判断落点是否是底部或已经有方块，如果有则落实
                    for (int i = 0; i < actPoint.length; i++) {
                        if (actPoint[i].y == 19 || mMap[actPoint[i].x][actPoint[i].y + 1]) {
                            flag = true;
                            todown();
                            break;
                        }
                    }
                    //方块下落一格
                    for (int i = 0; i < actPoint.length; i++) {
                        if (!flag)
                            actPoint[i].y += 1;
                    }
                    panelGame.repaint();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        gamethread.start();
    }

    //减少sleep时间，表现为方块下落加快
    public void TimeAdd() {
        if (sleeptime > 100)
            sleeptime -= 200;
    }

    //方块下落减慢
    public void TimeSub() {
        if (sleeptime < 900)
            sleeptime += 200;
    }

    //提示用户落点
    public void DrawHint(Graphics g) {
        int minX = 9;
        int maxX = 0;
        Image img = new ImageIcon("resources/images/bghint.png").getImage();
        //得到最左端和最右端
        for (int i = 0; i < actPoint.length; i++) {
            if (minX > actPoint[i].x)
                minX = actPoint[i].x;
            if (maxX < actPoint[i].x)
                maxX = actPoint[i].x;
        }
        for (int j = 0; j <= 19; j++) {
            g.drawImage(img, fontx + minX * size, fonty + j * size, (maxX - minX + 1) * size, size, null);
        }
    }

    //写入文档
    public void addpoint(String name) {
        File file = new File("resources/data.txt");
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
            String str = name + " " + totalpoint;
            try {
                BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true), StandardCharsets.UTF_8));
                bufferedWriter.write(str + "\r\n");
                bufferedWriter.flush();
                bufferedWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
    }


    //播放音乐
    public void musicPlay(String str) {
        if (str.equals("Background")) {
            String filepath = "resources/music/music1.mp3";
            MusicPlayer musicPlayer = new MusicPlayer(filepath, this);
            musicPlayer.loopPlay();
        }
        if (str.equals("Bingo")) {
            String filepath = "resources/music/bingo.mp3";
            MusicPlayer musicPlayer = new MusicPlayer(filepath, this);
            musicPlayer.Play();
        }

    }


    //开始游戏
    public void setStartGame() {
        StartGame = true;
    }

    //暂停游戏
    public void setPauseGame() {
        StartGame = false;
    }

    //获得是否正在游戏状态
    public Boolean getStartGame() {
        return StartGame;
    }

    //获取游戏是否失败

    public Boolean getAlreadyloose() {
        return alreadyloose;
    }

    //获得sleeptime时间
    public int getSleeptime() {
        return sleeptime;
    }

    //获得totalpoint总分数
    public int getTotalpoint() {
        return totalpoint;
    }

    //获得actcode当前方块形状
    public int getActcode() {
        return actcode;
    }

}
